﻿using LiteNetLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;

namespace Core
{
    public class UdpConnecterSystem : EntitySystem, IAwake, ILateUpdate
    {
        IEntityFilter clientFilter;
        IEntityEnumerable netManagerGroup;
        NetManager manager;
        internal Dictionary<NetPeer, UdpSession> peer2Sessions = new Dictionary<NetPeer, UdpSession>();

        public void Awake()
        {
            clientFilter = NewEntityFilter(typeof(UdpClientComponent), typeof(LiteNetManagerComponent));
            netManagerGroup = GetEntityGroup(clientFilter);

            Subscribe<InitUdpClient>(InitUdpClient);
            Subscribe<ConnectUdp>(ConnectUdp);
        }

        private void InitUdpClient(ref InitUdpClient eventData)
        {
            var listener = new EventBasedNetListener();
            manager = new NetManager(listener);
            manager.AutoRecycle = true;

            listener.PeerConnectedEvent += OnConnectCompleted;
            listener.NetworkReceiveEvent += OnReceiveCompleted;
            listener.PeerDisconnectedEvent += OnDisconnectCompleted;

            var clientCom = NewComponent<UdpClientComponent>();
            clientCom.listener = listener;

            var lnmCom = NewComponent<LiteNetManagerComponent>();
            lnmCom.manager = manager;

            var entity = NewEntity();
            entity.AddComponent(clientCom);
            entity.AddComponent(lnmCom);
        }

        private void OnDisconnectCompleted(NetPeer peer, DisconnectInfo disconnectInfo)
        {
            peer2Sessions.Remove(peer);
            new CoreTip($"UDP连接断开，Address:{peer.Port}, port:{peer.Port}").Publish();
        }

        private void OnReceiveCompleted(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod)
        {
            var messageBuffer = MessageBuffer.Pop();
            messageBuffer.messageLength = reader.AvailableBytes;
            reader.GetBytes(messageBuffer.buffer, reader.AvailableBytes);

            if (peer2Sessions.TryGetValue(peer, out var session))
            {
                HandleMessage(session, messageBuffer);
                MessageBuffer.Push(messageBuffer);
            }
            else
            {
                new CoreTip(TipLevel.Error, $"找不到处理消息会话，Address:{peer.Port}, port:{peer.Port}").Publish();
            }
        }

        internal void HandleMessage(UdpSession session, MessageBuffer messageBuffer)
        {
            var rpcToken = messageBuffer.buffer[Config.RPC_ID_POSITION];
            byte rpcID = (byte)(rpcToken & Config.RPC_ID_MASK);
            var message = new DeserializeMessage(messageBuffer.buffer, messageBuffer.messageLength).Publish().message;

            if (message == null)
            {
                new CoreTip(TipLevel.Error, $"序列化失败，无法处理消息。{session.SessionID}").Publish();
                return;
            }

            if (rpcToken > Config.RPC_ID_MASK)
            {
                // 处理rpc消息
                if (!session.rpcTasks.TryGetValue(rpcID, out var task))
                {
                    new CoreTip(TipLevel.Error, $"RPC回调处理未找到，rpcID:{rpcID},{message.GetType()}").Publish();
                    return;
                }

                new CoreTip($"处理RPC回调：rpcID:{rpcID}, sessionID:{session.SessionID}, message:{message.GetType()}, time:{DateTime.Now}").Publish();
                session.rpcTasks.Remove(rpcID);
                task.SetResult(message);
            }
            else
            {
                new CoreTip($"处理消息：rpcID:{rpcID}, sessionID:{session.SessionID}, message:{message.GetType()}, time:{DateTime.Now}").Publish();
                new HandleMessage(session, message, rpcID).Publish();
            }
        }

        private void OnConnectCompleted(NetPeer peer)
        {
            new CoreTip($"UDP链接成功，Address:{peer.Port}, port:{peer.Port}").Publish();

            var session = new UdpSession(peer, manager);
            session.Entity = NewEntity();
            peer2Sessions.Add(peer, session);

            new OnConnectUdpServerCompleted(session, true).Publish();
        }

        private void ConnectUdp(ref ConnectUdp eventData)
        {
            var entity = GetEntity(clientFilter);
            if (entity == null)
            {
                new CoreTip(TipLevel.Error, "尚未初始化UDP客户端, 请先调用InitUdpClient").Publish();
                return;
            }

            var lnmCom = entity.GetComponent<LiteNetManagerComponent>();

            if (!lnmCom.manager.Start())
            {
                new CoreTip(TipLevel.Error, "UDP启动失败").Publish();
                return;
            }

            lnmCom.manager.Connect(eventData.address, eventData.port, "");
        }


        public void LateUpdate()
        {
            foreach (var item in netManagerGroup)
            {
                var lnmCom = item.GetComponent<LiteNetManagerComponent>();
                lnmCom.manager.PollEvents();
            }
        }
    }

    public class UdpClientComponent : EntityComponent
    {
        public EventBasedNetListener listener;
    }

    public struct InitUdpClient : IEventData
    {

    }

    public struct ConnectUdp : IEventData
    {
        public string address;
        public int port;

        public ConnectUdp(string address, int port)
        {
            this.address = address;
            this.port = port;
        }
    }

    public struct OnConnectUdpServerCompleted : IEventData
    {
        public ISession session;
        public bool isConnect;

        public OnConnectUdpServerCompleted(ISession session, bool isConnect)
        {
            this.session = session;
            this.isConnect = isConnect;
        }
    }
}
